Kernel.sandbox.define('main', {

/* ################################################################
## DOM manipulation MUST NOT be done here.
## If you need to manipulate the DOM use the broadcast() method
## and do the manipulation on the listening side.
## ############################################################## */

	/*******  Module Properties  *******/
	mRunningList: [],
	mRunningTitle: '',
	mRunningFSA: '',
		
	/*******  Audio Properties  *******/
	audioInstance: '',
	audioStartVolume: 50,
	audioPlayProgress: false,
	audioPlaying: false,
	audioPlayingID: '',
	audioPlayingURL: '',
	audioPlayingArt: '',
	audioPlayingTitle: '',
	audioPlayingArtist: '',
	audioPlayingAlbum: '',
	audioPlayingGenre: '',
	
	/*******  Position Properties  *******/
	gpsThread: '',	
	gpsStarted: false,
	gpsBusy: false,
	gpsPacket: '',
	gpsJSON: {},


	/**
	 * Inject new CSS file real-time without refresh
	 * @param {string} scriptFile Relative URI of the script file.
	 */
	injectStyle: function(scriptFile) {
		var headID = document.getElementsByTagName("head")[0];         
		var cssNode = document.createElement('link');
		cssNode.type = 'text/css';
		cssNode.rel = 'stylesheet';
		cssNode.href = scriptFile;
		cssNode.media = 'screen';
		headID.appendChild(cssNode);		
	},
	
	/**
	 * Inject new JS file style real-time without refresh
	 * @param {string} scriptFile Relative URI of the script file.
	 */
	injectScript: function(scriptFile) {
		var headID = document.getElementsByTagName("head")[0];         
		var newScript = document.createElement('script');
		newScript.type = 'text/javascript';
		newScript.src = scriptFile;
		headID.appendChild(newScript);		
	},

	/**
	 * Play the specified audio file, if supported
	 * @constructor
	 * @param {string} mediaFile Relative URI of the audio file to play.
	 * @param {integer} startvol Volume to play.
	 * @param {string} art Relative URI of cover art.
	 * @param {string} title Title name of the audio file.
	 * @param {string} artist Artist name of the audio file.
	 * @param {string} album Album name of the audio file.
	 * @param {string} genre Genre name of the audio file.
	 */			
	audioPlay: function(mediaFile, startvol, art, title, artist, album, genre) {
		var self = this;

		if (!self.audioPlayProgress) {
			self.audioPlayProgress = true;
			if (self.audioPlaying) {
				self.audioInstance.unbind("canplay timeupdate ended");
				self.audioInstance.fadeOut(1000, function() {
					self.audioInstance.stop();
					self.audioInstance = new buzz.sound(mediaFile.replace("\\'", "'"), {
						webAudioApi: false
					});		
					self.audioBind(mediaFile, startvol, art, title, artist, album, genre);
				});
			} else {
				self.audioInstance = new buzz.sound(mediaFile.replace("\\'", "'"), {
					webAudioApi: false
				});		
				self.audioBind(mediaFile, startvol, art, title, artist, album, genre);
			}
		}
 	},
 	
 	audioBind: function(mediaFile, startvol, art, title, artist, album, genre) {
		var self = this;		

		self.audioInstance.bind("loadedmetadata", function(e) {
			self.audioPlayingURL = mediaFile;
			self.audioStartVolume = startvol;

			self.audioPlayingTitle = title.replace(".mp3", "");
			self.audioPlayingArtist = artist;
			self.audioPlayingAlbum = album;
			self.audioPlayingGenre = genre;
			self.audioPlayingArt = art;
		});

		self.audioInstance.bind("canplay", function(e) {
			self.audioInstance.setVolume(parseInt(self.audioStartVolume));
			self.audioInstance.fadeTo(parseInt(self.audioStartVolume), 1000, function() {
				self.audioPlaying = true;
				self.audioPlayProgress = false;
			});
			self.broadcast("audio started", self.audioPlayingTitle);

			self.audioInstance.bind("timeupdate", function(e) {
				var timer = this.getPercent();
				self.broadcast("audio progressupdate", parseInt(timer));
			});
			
			self.audioInstance.bind("volumechange", function() {
				self.broadcast("audio volumechange", this.getVolume());
			});

			self.audioInstance.bind("ended", function(e) {
				self.broadcast("audio end", "true");
				self.audioPlaying = false;
				self.audioPlayProgress = false;
				self.audioInstance = '';
			});
		});

		self.audioInstance.bind("error", function(e) {
			self.broadcast("SYSTEM-ERROR", "Error playing audio, try again later");
			self.audioPlaying = false;
			self.audioPlayProgress = false;
		});
	},
 	
	/**
	 * Pause the audio if playing.
	 */			
 	audioPause: function() {
		var self = this;		

 	},
 	
	/**
	 * Stop the audio if playing.
	 */			
 	audioStop: function() {
		var self = this;		

		if (self.audioPlaying) {
			self.audioInstance.unbind("canplay timeupdate error ended");
			self.audioInstance.fadeOut(1000, function() {
				for (var i in buzz.sounds) {
					buzz.sounds[i].stop();
				}
				self.audioPlaying = false;
				self.audioPlayingURL = '';
				self.audioPlayingArt = '';
				self.audioPlayingTitle = '';
				self.audioPlayingArtist = '';
				self.audioPlayingAlbum = '';
				self.audioPlayingGenre = '';
				self.broadcast("audio end", "true");
			});
		}
	},
	
	/**
	 * Change the current audio volume.
	 * @param {integer} newVolume New value of the volume
	 */			
	audioVolume: function(newVolume) {
		var self = this;
		
		self.audioInstance.setVolume(parseInt(newVolume));
		localStorage.setItem("CONFIG_STARTUP_VOLUME", parseInt(newVolume));
		self.audioStartVolume = parseInt(newVolume);
		self.broadcast("audio volumeupdate", parseInt(newVolume));
	},
 			
	/**
	 * Mute the audio volume.
	 */			
	audioToggleMute: function() {
		var self = this;		
		
		self.audioInstance.toggleMute();
	},
	
	/**
	 * Seek to specified time in audio, if playing.
	 * @param {integer} newTime Value in time (00:00) to seek audio to
	 */			
	audioPositionTime: function(newTime) {
		var self = this;
		
		self.audioInstance.setTime(newTime);
	},
	
	/**
	 * Seek to specified percent in audio, if playing.
	 * @param {integer} newPercent Value in percent (50) to seek audio to
	 */			
	audioPositionPercent: function(newPercent) {
		var self = this;
		
		self.audioInstance.setPercent(newPercent);
	},
	
	/**
	 * Returns URI of the audio file, if playing.
	 */			
	audioGetPlayingURL: function() {
		var self = this;
		return self.audioPlayingURL;
	},
	
	/**
	 * Returns title name of the audio file, if playing.
	 */			
	audioGetPlayingTitle: function() {
		var self = this;
		return self.audioPlayingTitle;
	},
	
	/**
	 * Returns TRUE if audio is playing.
	 */			
	audioIsPlaying: function() {
		var self = this;
		if (self.audioInstance.isEnded()) {
			return false;
		} else {
			return true;
		}
	},
	
	/**
	 * Returns TRUE if audio volume is muted.
	 */			
	audioIsMute: function() {
		var self = this;
		return self.audioInstance.isMuted();
	},
	
	/**
	 * Start the GPS process.
	 * Using NMEA 0183 & GPSD Specifications.
	 * @constructor
	 */				
	vehiclePositioningStart: function() {
		var self = this;
		
		if (!self.gpsStarted) {
			self.gpsThread = new Worker("js/workers/gpsd_poll.js");
			self.gpsStarted = true;

			self.gpsThread.onmessage = function(event) {
				if (event.data !== "") {
					try {
						if (event.data !== "nofix") {
							gpsdJSON = JSON.parse(event.data);
							if (gpsdJSON.class !== "VERSION") {
								// mode1 is nofix, mode2 & 3 is 2D/3D fix
								if (gpsdJSON.mode > 1 || gpsdJSON.class == 'SKY') {
									self.broadcast('gps-nofix', "");
									self.gpsPacket = event.data;
									self.gpsJSON = JSON.parse(self.gpsPacket);

								    if (gpsdJSON.class !== "SKY") {
										self.vehiclePositionAccuracy();
										self.vehiclePosition();
										self.vehicleCourse();
										self.vehicleAltitude();
										self.vehicleSpeed();
										self.vehicleGPSTime();
									} else {
										// process SKY packets
										self.broadcast('satellites', gpsdJSON.satellites.length);
									}
								} else {
									self.broadcast('gps-nofix', "nofix");
								}
							} else {
								// ignore VERSION packets
							}
						} else {
							self.broadcast('gps-nofix', "nofix");
						}
					} catch(e) {
						//self.broadcast('SYSTEM-ERROR', "Vehicle Position Error Detected. Applying self healing.");
					}					
				} else {
					self.broadcast('gps-nofix', "nofix");
				}
			}; // end of gpsThread.message	
		} 
	},
	
	/**
	 * Broadcast next GPS time when packet available.
	 */			
	vehicleGPSTime: function() {
		var self = this;
		var gpsTime = self.gpsJSON.time;

		gpsTime = gpsTime.split("T"); // split time and date
		gpsTime = gpsTime[1].split(":"); // tokenize time
		self.broadcast('gps-time', gpsTime[0]+":"+gpsTime[1]);
	},
	
	/**
	 * Broadcast next GPS position (lat:lon) when packet available.
	 */			
	vehiclePosition: function() {
		var self = this;
		var position = self.gpsJSON.lat+":"+self.gpsJSON.lon;
		
		self.broadcast('gps-position', position);
		return position;
	},
	
	/**
	 * Broadcast next GPS altitude when packet available.
	 */			
	vehicleAltitude: function() {
		var self = this;

		self.broadcast('gps-altitude', self.gpsJSON.alt+" m");
		return self.gpsJSON.alt+" m";
	},
	
	/**
	 * Broadcast next GPS direction (in degrees) when packet available.
	 */			
	vehicleCourse: function() {
		var self = this;

		if (parseInt(self.gpsJSON.track) >= 0) {
			self.broadcast('gps-course', self.gpsJSON.track);
			return self.gpsJSON.track;
		}
	},
		
	/**
	 * Broadcast next GPS speed when packet available.
	 */			
	vehicleSpeed: function() {
		// make this imperial/metric compatible
		var self = this;
		var speed = parseInt(self.gpsJSON.speed) * 3.6;
		
		if (speed < 0.9) speed = 0;
		
		self.broadcast('gps-speed', speed.toFixed(0)+" km/h");
		return speed.toFixed(0)+" km/h";
	},

	/**
	 * Broadcast GPS position accuracy when packet available.
	 * Return values:
	 *     vg - accurate to 5m    
	 *      g - accurate to 15m
	 *      a - accurate to 25m
	 *      p - accurate to over 25m
	 */			
	vehiclePositionAccuracy: function() {
		var self = this;
		var rt = "";

		if (self.gpsJSON.epx < 6 && gpsdJSON.epy < 6) {
			rt = "vg";
			self.broadcast('gps-accuracy', rt);
		} else if (self.gpsJSON.epx < 16 && gpsdJSON.epy < 16) {
			rt = "g";
			self.broadcast('gps-accuracy', rt);
		} else if (self.gpsJSON.epx < 26 || gpsdJSON.epy < 26) {
			rt = "a";
			self.broadcast('gps-accuracy', rt);
		} else {			
			rt = "p";
			self.broadcast('gps-accuracy', rt);
		}
		return rt;
	},
	
	vehicleSatellitesLocked: function() {
		var self = this;
		
	}, 
	
	gpsLatitudeFault: function() {

	},
	
	gpsLongitudeFault: function() {
		
	},
	
	gpsAltitudeFault: function() {
		
	},

	
	/**
	 * Stop the GPS process and all child threads.
	 */			
	vehiclePositionStop: function() {
		var self = this;
		
		if (self.gpsStarted) {
			self.gpsStarted = false;
			self.gpsThread.terminate();
			self.gpsThread = undefined;
		}
	},
	
	/* ###################################################
	 * 
	 * ###################################################*/
	  


	/* ################################
	## Getter & Setters
	#################################*/
				
	putmRunningTitle: function(newTitle) {
		var self = this;
		self.mRunningTitle = newTitle;
	},
	
	putmRunningFSA: function(newFSA) {
		var self = this;
		self.mRunningFSA = newFSA;
	},	
	
	getmRunningTitle: function() {
		var self = this;
		return self.mRunningTitle;
	},
	
	getmRunningFSA: function() {
		var self = this;
		return self.mRunningFSA;
	},
	
	
	/* ################################
	## Module Messaging Library
	#################################*/
				
	refreshStatusFeed: function() {
		var self = this;
		
		Kernel.doREST({
			type: 'GET',
			url: 'feed',
			success: function(data) {
				self.broadcast('status-feed-update', data);
			},
			failure: function(er) {
				
				// NOTE: No module in this example listens for error messages to errors will go unnoticed
				self.broadcast('error', "Couldn't fetch the feed: "+er);
			}
		});
	},
	
	updateStatus: function(status) {
		var self = this;
		
		Kernel.doREST({
			type: 'POST',
			url: 'status',
			params: { newstatus: status },
			success: function(data) {
				
				// Let everyone know that you updated your status
				self.broadcast('status-update', status);
				
				// Also update the feed
				self.refreshStatusFeed();
			},
			failure: function(er) {
				self.broadcast('error', "Couldn't update the status: "+er);
			}
		});
	}
});
    

